import sys
import logging
import os
import json
from PyQt6.QtWidgets import (
    QApplication,
    QMainWindow,
    QSplitter,
    QWidget,
    QVBoxLayout,
    QHBoxLayout,
    QPushButton,
    QFileDialog,
    QStatusBar,
    QMessageBox,
    QFormLayout,
    QScrollArea,
    QStackedWidget,
    QTextEdit,
    QProgressBar,
    QTreeWidgetItem,
    QLabel,
)
from PyQt6.QtCore import Qt, QTimer
from concurrent.futures import ThreadPoolExecutor
from parser.kconfiglib_adapter import KconfiglibAdapter
from parser.config_model import ConfigItem, MenuItem
from gui.container_modules.menu_module import MenuModule
from gui.container_modules.config_container import ConfigContainerModule
from gui.container_modules.comment_module import CommentModule
from gui.container_modules.choice_module import ChoiceModule
from gui.property_modules.dependency_module import DependencyModule
from gui.property_modules.visibility_module import VisibilityModule
from gui.property_modules.default_module import DefaultModule
from gui.property_modules.range_module import RangeModule
from parser.config_generator import ConfigGeneratorModule
from parser.config_loader import ConfigLoaderModule
from gui.env_var_dialog import EnvVarDialog
from PyQt6.QtWidgets import QCheckBox, QComboBox, QLineEdit, QSpinBox



class KconfigMainWindow(QMainWindow):
    """主窗口"""

    def __init__(self):
        super().__init__()
        self.setWindowTitle("Kconfig GUI Editor")
        self.setGeometry(100, 100, 1200, 800)

        # 初始化模块
        self.init_modules()

        # 初始化UI
        self.init_ui()

        # 初始化解析器
        self.adapter = KconfiglibAdapter()
        self.parser = None
        self.config_values = {}
        self.current_file = None
        # 记录已连接信号的控件，避免重复连接
        self._connected_controls = set()

        # 使用 ThreadPoolExecutor 作为后台解析器执行器，避免 QThread 销毁警告
        self._executor = ThreadPoolExecutor(max_workers=1)
        self._parse_future = None
        self._parse_poll_timer = None
        # background compute future for defaults/eval
        self._compute_future = None
        self._compute_poll_timer = None

    def init_modules(self):
        """
        初始化所有模块
        """
        # 容器模块
        self.config_container_module = ConfigContainerModule()
        self.menu_module = MenuModule()
        self.comment_module = CommentModule()
        self.choice_module = ChoiceModule()

        # 属性模块
        self.dependency_module = DependencyModule(self.config_container_module)
        self.visibility_module = VisibilityModule(self.config_container_module)
        self.default_module = DefaultModule()
        self.range_module = RangeModule()

        # 连接信号
        try:
            self.range_module.valueOutOfRange.connect(self.on_value_out_of_range)
        except Exception:
            pass

    def init_ui(self):
        """初始化UI"""
        # 创建主控件
        main_widget = QWidget()
        self.setCentralWidget(main_widget)

        # 创建主布局
        main_layout = QVBoxLayout(main_widget)
        main_layout.setContentsMargins(0, 0, 0, 0)

        # 创建工具栏
        toolbar = self.addToolBar("File")
        open_action = toolbar.addAction("Open Kconfig File")
        save_action = toolbar.addAction("Save Configuration")
        # 修复：打开 Kconfig 文件应触发解析流程，而不是加载 .config
        open_action.triggered.connect(self.open_kconfig_file)
        save_action.triggered.connect(self.save_config)

        # 创建分割器
        splitter = QSplitter(Qt.Orientation.Horizontal)

        # 左侧菜单面板
        self.menu_panel = QWidget()
        menu_layout = QVBoxLayout(self.menu_panel)
        menu_layout.setContentsMargins(0, 0, 0, 0)
        # 将菜单树加入左侧面板
        menu_layout.addWidget(self.menu_module.menu_tree)

        # 右侧配置面板 - 改为基于 QStackedWidget 的分页，每个 menu/submenu 使用自己的 page

        # 一个默认页面（用于未分类或临时内容）
        self._default_page = QWidget()
        # 使用垂直布局避免表单布局在跨列/标题行上产生紧贴或重叠
        self._default_layout = QVBoxLayout(self._default_page)
        self._default_layout.setContentsMargins(8, 8, 8, 8)
        self._default_layout.setSpacing(12)
        self._default_layout.setSpacing(12)

        # Stacked widget 保存每个 menu 的 page
        self.stacked_pages = QStackedWidget()
        self.stacked_pages.addWidget(self._default_page)

        # keep a map: menu_id -> (page_widget, layout)
        self.menu_pages = {}
        # map config_id -> parent menu id (used to route config to the correct page)
        self.config_to_menu = {}
        # map choice_id -> parent menu id (used to route choice clicks to the right page)
        self.choice_to_menu = {}
        # submenu anchors for scroll-to
        self.menu_anchor_widgets = {}
        # map any menu_id (including submenus) to its root top-level menu id
        self.menu_to_root = {}
        # track which menus have had their subtree rendered
        self.rendered_menu_ids = set()
        # 记录 config 的父 choice（用于点击子项时不重复创建容器）
        self.config_choice_parent = {}

        # wrap stacked widget in a scroll area for better UX with many items
        self.config_scroll = QScrollArea()
        self.config_scroll.setWidgetResizable(True)
        self.config_scroll.setWidget(self.stacked_pages)

        splitter.addWidget(self.menu_panel)
        splitter.addWidget(self.config_scroll)
        splitter.setSizes([260, 940])

        main_layout.addWidget(splitter)

        # 创建状态栏
        self.status_bar = QStatusBar()
        self.setStatusBar(self.status_bar)

        # 添加简单日志面板和进度条
        self.log_panel = QTextEdit()
        self.log_panel.setReadOnly(True)
        # more compact log panel height
        self.log_panel.setFixedHeight(80)
        self.progress_bar = QProgressBar()
        self.progress_bar.setRange(0, 100)
        self.progress_bar.setValue(0)

        # 将日志面板和进度条添加到主布局底部
        main_layout.addWidget(self.log_panel)
        main_layout.addWidget(self.progress_bar)

        # 连接菜单点击信号
        self.menu_module.menu_tree.configItemClicked.connect(self.on_config_item_clicked)

    def open_kconfig_file(self):
        """选择并解析 Kconfig 文件"""
        file_dialog = QFileDialog()
        file_path, _ = file_dialog.getOpenFileName(
            self,
            "Open Kconfig File",
            "",
            "Kconfig Files (Kconfig *.Kconfig);;All Files (*)",
        )
        if not file_path:
            return
        self.current_file = file_path
        self.start_parse_in_background(file_path)

    def start_parse_in_background(self, file_path: str):
        """后台解析 Kconfig，并在完成后填充菜单树与页面。"""
        try:
            # 若存在正在解析的任务，尽量终止并重置执行器，避免结果交叉
            if getattr(self, '_parse_future', None) and not self._parse_future.done():
                try:
                    self._parse_future.cancel()
                    self._executor.shutdown(wait=False, cancel_futures=True)
                except Exception:
                    logging.exception("Failed to cancel previous parse task")
                finally:
                    try:
                        self._executor = ThreadPoolExecutor(max_workers=1)
                        self._parse_future = None
                    except Exception:
                        logging.exception("Failed to reset executor")

            # 清理现有 UI 与树，避免重复累积
            try:
                self.clear_config_panel()
                self.menu_module.menu_tree.clear()
                self.menu_module.menu_items.clear()
            except Exception:
                logging.exception("Failed to clear existing UI/tree before parsing")

            # 重置解析器状态，确保不会在老的 parser 上累加
            try:
                self.adapter = KconfiglibAdapter()
                self.parser = None
                self.config_values = {}
            except Exception:
                logging.exception("Failed to reset parser instance")
                QMessageBox.critical(self, "Error", "Parser reset failed; please check logs.")
                return

            self.status_bar.showMessage(f"Parsing {file_path}...")
            self.progress_bar.setRange(0, 0)  # 不确定时显示忙碌状态

            # 提交后台任务
            try:
                # 构建解析所需环境变量
                env_path = os.path.join(os.path.dirname(__file__), 'kconfig_env.json')
                var_map = {}
                if os.path.isfile(env_path):
                    try:
                        with open(env_path, 'r', encoding='utf-8') as f:
                            var_map.update(json.load(f) or {})
                    except Exception:
                        logging.exception("Failed to load kconfig_env.json")
                # 补充系统环境变量
                for k in ['IDF_PATH', 'IDF_TARGET']:
                    if not var_map.get(k) and os.environ.get(k):
                        var_map[k] = os.environ.get(k)
                # 如果仍缺失，弹窗获取
                missing = [k for k in ['IDF_PATH', 'IDF_TARGET'] if not var_map.get(k)]
                if missing:
                    try:
                        dlg = EnvVarDialog(missing, parent=self)
                        if dlg.exec() == dlg.DialogCode.Accepted:
                            values = dlg.get_values()
                            var_map.update(values)
                            # 保存到 kconfig_env.json，便于下次使用
                            try:
                                with open(env_path, 'w', encoding='utf-8') as f:
                                    json.dump(var_map, f, indent=2)
                            except Exception:
                                logging.exception("Failed to save kconfig_env.json")
                        else:
                            QMessageBox.warning(self, "Warning", f"Missing environment variables: {', '.join(missing)}")
                            self.progress_bar.setRange(0, 100)
                            self.progress_bar.setValue(0)
                            return
                    except Exception:
                        logging.exception("Failed to collect missing env vars")
                # 搜索路径：优先 IDF_PATH，其次当前文件目录
                search_paths = []
                if var_map.get('IDF_PATH'):
                    search_paths.append(var_map['IDF_PATH'])
                try:
                    search_paths.append(os.path.dirname(os.path.abspath(file_path)))
                except Exception:
                    logging.exception("Failed to add file directory to search_paths")
                self._parse_future = self._executor.submit(
                    self.adapter.parse_file, file_path, search_paths=search_paths, var_map=var_map
                )
            except Exception:
                logging.exception("Failed to submit parse task")
                self.progress_bar.setRange(0, 100)
                self.progress_bar.setValue(0)
                QMessageBox.critical(self, "Error", "Failed to start parsing. See logs for details.")
                return

            # 轮询完成并在主线程更新 UI
            if getattr(self, '_parse_poll_timer', None):
                try:
                    self._parse_poll_timer.stop()
                except Exception:
                    logging.exception("Failed to stop previous parse poll timer")
            self._parse_poll_timer = QTimer()

            def poll_done():
                if not getattr(self, '_parse_future', None):
                    return
                if self._parse_future.done():
                    # 解析完成
                    try:
                        res = self._parse_future.result()
                        wrapper = None
                        items = []
                        if isinstance(res, tuple):
                            if res and res[0] is not None:
                                wrapper, items = res[0], res[1]
                            else:
                                meta = res[2] if len(res) > 2 else {}
                                raise RuntimeError(meta.get('error', 'Parse failed'))
                        else:
                            items = res if isinstance(res, list) else []
                            wrapper = self.adapter
                        self.parser = wrapper
                    except Exception as e:
                        logging.exception("Parse task failed")
                        self.progress_bar.setRange(0, 100)
                        self.progress_bar.setValue(0)
                        QMessageBox.critical(self, "Error", f"Failed to parse: {e}")
                        try:
                            self._parse_poll_timer.stop()
                        except Exception:
                            logging.exception("Failed to stop poll timer after error")
                        return

                    # 填充左侧菜单树（顶级菜单）
                    try:
                        self.menu_module.menu_tree.clear()
                        top_menus = [m for m in self.parser.menu_items.values() if not getattr(m, 'parent', None)]
                        root_item = self.menu_module.menu_tree.invisibleRootItem()
                        for m in top_menus:
                            self.menu_module.add_menu_item(m, root_item)
                        # Fallback: if no menus parsed but configs exist, create a synthetic root menu
                        if not top_menus and getattr(self.parser, 'config_items', None):
                            try:
                                from parser.config_model import MenuItem
                            except Exception:
                                MenuItem = None
                            try:
                                if MenuItem is not None:
                                    auto_id = "menu-auto-root"
                                    auto_menu = MenuItem(id=auto_id, name="auto_root", prompt="全部配置")
                                    for cfg in self.parser.config_items.values():
                                        try:
                                            auto_menu.children.append(cfg)
                                        except Exception:
                                            pass
                                    try:
                                        self.parser.menu_items[auto_id] = auto_menu
                                    except Exception:
                                        pass
                                    try:
                                        self.menu_module.add_menu_item(auto_menu, root_item)
                                    except Exception:
                                        logging.exception("Failed to add auto root menu to tree")
                                    # update top_menus so following page creation renders this synthetic menu
                                    top_menus = [auto_menu]
                            except Exception:
                                logging.exception("Failed to create synthetic root menu for configs-only parse")
                    except Exception:
                        logging.exception("Failed to populate menu tree")
                        QMessageBox.warning(self, "Warning", "Menu tree population failed; see logs.")

                    # 为顶级菜单创建页面；首次渲染第一个页面，其他页面懒加载
                    try:
                        first_page_id = None
                        for m in top_menus:
                            if m.id not in self.menu_pages:
                                page = QWidget()
                                page_layout = QVBoxLayout(page)
                                page_layout.setContentsMargins(8, 8, 8, 8)
                                page_layout.setSpacing(12)
                                self.stacked_pages.addWidget(page)
                                self.menu_pages[m.id] = (page, page_layout)
                            if first_page_id is None:
                                first_page_id = m.id
                        # 渲染首个页面的子树，提升“有内容”的反馈
                        def render_menu_subtree(parent_menu_id: str, children: list, page: QWidget, layout: QVBoxLayout):
                            for child in children:
                                if isinstance(child, MenuItem):
                                    self.menu_to_root[child.id] = parent_menu_id
                                    header = QLabel(child.prompt)
                                    header.setStyleSheet('font-weight: bold; padding-top: 8px;')
                                    self.menu_anchor_widgets[child.id] = header
                                    layout.addWidget(header)
                                    layout.addSpacing(6)
                                    render_menu_subtree(parent_menu_id, child.children, page, layout)
                                elif isinstance(child, ConfigItem):
                                    self.config_to_menu[child.id] = parent_menu_id
                                    container = self.config_container_module.containers.get(child.id)
                                    if container is None:
                                        container = self.config_container_module.create_container(child, page)
                                    if layout.indexOf(container) == -1:
                                        layout.addWidget(container)
                                        layout.addSpacing(4)
                                elif hasattr(child, 'text') and getattr(child, 'text', None) is not None:
                                    comment_widget = self.comment_module.create_comment(child, page)
                                    layout.addWidget(comment_widget)
                                    layout.addSpacing(4)
                                else:
                                    self.choice_to_menu[child.id] = parent_menu_id
                                    group = self.choice_module.create_choice_group(child, page)
                                    for cfg in getattr(child, 'children', []) or []:
                                        self.config_choice_parent[cfg.id] = child.id
                                    layout.addWidget(group)
                                    layout.addSpacing(6)
                        if first_page_id:
                            page, layout = self.menu_pages.get(first_page_id)
                            root_menu = self.parser.menu_items.get(first_page_id)
                            if page and layout and root_menu:
                                render_menu_subtree(root_menu.id, root_menu.children, page, layout)
                                self.rendered_menu_ids.add(root_menu.id)
                                self.stacked_pages.setCurrentWidget(page)
                    except Exception:
                        logging.exception("Failed to create/render pages")
                        QMessageBox.warning(self, "Warning", "Page rendering failed; see logs.")

                    # 完成
                    try:
                        self._parse_poll_timer.stop()
                    except Exception:
                        logging.exception("Failed to stop poll timer")
                    self.progress_bar.setRange(0, 100)
                    self.progress_bar.setValue(100)
                    unknown = 0
                    try:
                        unknown = getattr(self.parser, '_unknown_lines_count', 0) or 0
                    except Exception:
                        logging.exception("Failed to read unknown line count from parser")
                    self.status_bar.showMessage(
                        f"Parsed {len(self.parser.menu_items)} menus, {len(self.parser.config_items)} configs from {file_path}" + (f"; unknown lines: {unknown}" if unknown else "")
                    )

            self._parse_poll_timer.timeout.connect(poll_done)
            self._parse_poll_timer.start(50)
        except Exception as e:
            logging.exception("Failed to start parse")
            QMessageBox.critical(self, "Error", f"Failed to start parse: {e}")

    def _render_menu_page(self, menu_id: str) -> None:
        """在需要时为顶级菜单页面进行懒加载渲染（子树 -> page）。"""
        try:
            if menu_id in self.rendered_menu_ids:
                return
            menu_item = self.parser.menu_items.get(menu_id)
            if not menu_item:
                return
            page_tuple = self.menu_pages.get(menu_id)
            if not page_tuple:
                return
            page, layout = page_tuple

            # 顶级菜单自映射到 root，便于子菜单回溯
            try:
                self.menu_to_root[menu_id] = menu_id
            except Exception:
                pass

            def render_menu_subtree(parent_menu_id: str, children: list, page: QWidget, layout: QVBoxLayout):
                for child in children:
                    if isinstance(child, MenuItem):
                        # 记录子菜单到其顶层 root 的映射，并添加分节标题作为锚点
                        try:
                            self.menu_to_root[child.id] = parent_menu_id
                        except Exception:
                            pass
                        try:
                            header = QLabel(child.prompt)
                            header.setStyleSheet('font-weight: bold; padding-top: 8px;')
                            self.menu_anchor_widgets[child.id] = header
                            layout.addWidget(header)
                            # 在分节标题与其后内容之间增加间距，避免视觉重叠
                            layout.addSpacing(6)
                        except Exception:
                            pass
                        # 递归渲染子菜单内容
                        try:
                            render_menu_subtree(parent_menu_id, child.children, page, layout)
                        except Exception:
                            logging.exception('Failed to render submenu subtree: %s', getattr(child, 'id', '<noid>'))
                    elif isinstance(child, ConfigItem):
                        # 记录 config -> 顶级菜单映射，并预先创建容器
                        try:
                            self.config_to_menu[child.id] = parent_menu_id
                        except Exception:
                            pass
                        try:
                            container = self.config_container_module.containers.get(child.id)
                            if container is None:
                                container = self.config_container_module.create_container(child, page)
                            if layout.indexOf(container) == -1:
                                layout.addWidget(container)
                                # 容器之间增加少量间距，避免压迫到注释或下一控件
                                layout.addSpacing(4)
                        except Exception:
                            logging.exception('Failed to pre-render container for %s', getattr(child, 'id', '<noid>'))
                    elif hasattr(child, 'text') and getattr(child, 'text', None) is not None:
                        # 注释
                        try:
                            comment_widget = self.comment_module.create_comment(child, page)
                            layout.addWidget(comment_widget)
                            layout.addSpacing(4)
                        except Exception:
                            logging.exception('Failed to pre-render comment for %s', getattr(child, 'id', '<noid>'))
                    else:
                        # choice 组
                        try:
                            self.choice_to_menu[child.id] = parent_menu_id
                        except Exception:
                            pass
                        try:
                            group = self.choice_module.create_choice_group(child, page)
                            try:
                                for cfg in getattr(child, 'children', []) or []:
                                    self.config_choice_parent[cfg.id] = child.id
                            except Exception:
                                pass
                            layout.addWidget(group)
                            layout.addSpacing(6)
                        except Exception:
                            logging.exception('Failed to pre-render choice group for %s', getattr(child, 'id', '<noid>'))

            # 渲染当前顶级菜单整棵子树
            render_menu_subtree(menu_id, menu_item.children, page, layout)
            # 标记已渲染，避免重复
            self.rendered_menu_ids.add(menu_id)
        except Exception:
            logging.exception('Failed to render menu page: %s', menu_id)


    def register_properties(self):
        """注册配置项属性"""
        for config_item in self.parser.config_items.values():
            # 注册依赖条件
            self.dependency_module.register_dependency(config_item)

            # 注册可见性条件
            self.visibility_module.register_visibility_condition(config_item)

            # 注册默认值
            self.default_module.register_default_value(config_item)

            # 注册范围限制
            self.range_module.register_range_restriction(config_item)

    def on_config_item_clicked(self, config_id: str):
        """配置项点击事件"""
        # If this id corresponds to a menu page, switch to that page
        try:
            if config_id in self.menu_pages:
                page, layout = self.menu_pages[config_id]
                # 总是先尝试懒加载渲染该菜单页
                try:
                    self._render_menu_page(config_id)
                except Exception:
                    pass
                try:
                    self.stacked_pages.setCurrentWidget(page)
                    # scroll to top for menu page
                    try:
                        self.config_scroll.verticalScrollBar().setValue(0)
                    except Exception:
                        pass
                except Exception:
                    pass
                # 如果渲染后仍为空，二次尝试渲染
                try:
                    if layout is not None and layout.count() == 0:
                        self._render_menu_page(config_id)
                    # 若仍为空，记录状态栏提示，方便定位
                    if layout is not None and layout.count() == 0:
                        try:
                            menu_obj = self.parser.menu_items.get(config_id)
                            label = getattr(menu_obj, 'prompt', None) or getattr(menu_obj, 'name', None) or config_id
                            self.status_bar.showMessage(f"Menu '{label}' has no content")
                        except Exception:
                            pass
                except Exception:
                    pass
                return
        except Exception:
            pass

        # If this is a submenu id, scroll to its anchor within the root menu page
        try:
            if config_id in self.menu_anchor_widgets:
                root_id = self.menu_to_root.get(config_id)
                if root_id and root_id in self.menu_pages:
                    page, _ = self.menu_pages[root_id]
                    self.stacked_pages.setCurrentWidget(page)
                    anchor = self.menu_anchor_widgets.get(config_id)
                    try:
                        # ensure anchor visible
                        self.config_scroll.ensureWidgetVisible(anchor)
                    except Exception:
                        pass
                return
        except Exception:
            pass

        # If this is a choice id, switch to its parent menu page and scroll to group
        try:
            parent_menu_id = self.choice_to_menu.get(config_id)
            if parent_menu_id and parent_menu_id in self.menu_pages:
                page, _ = self.menu_pages[parent_menu_id]
                try:
                    self.stacked_pages.setCurrentWidget(page)
                    group = self.choice_module.choice_groups.get(config_id)
                    if group:
                        try:
                            self.config_scroll.ensureWidgetVisible(group)
                        except Exception:
                            pass
                except Exception:
                    pass
                return
        except Exception:
            pass

        # If this is a config that belongs to a choice, scroll to its radio within the choice group
        try:
            choice_id = getattr(self, 'config_choice_parent', {}).get(config_id)
            if choice_id:
                parent_menu_id = self.choice_to_menu.get(choice_id)
                if parent_menu_id and parent_menu_id in self.menu_pages:
                    page, _ = self.menu_pages[parent_menu_id]
                    self.stacked_pages.setCurrentWidget(page)
                    group = self.choice_module.choice_groups.get(choice_id)
                    if group:
                        try:
                            # 找到对应radio并滚动
                            radio_map = self.choice_module.radio_buttons.get(choice_id, {})
                            radio = radio_map.get(config_id)
                            target = radio if radio is not None else group
                            self.config_scroll.ensureWidgetVisible(target)
                        except Exception:
                            pass
                return
        except Exception:
            pass

        # Otherwise treat it as a config id and show its container on the parent menu page
        config_item = self.parser.config_items.get(config_id)
        if config_item:
            self.show_config_item(config_item)
            try:
                container = self.config_container_module.containers.get(config_item.id)
                if container:
                    self.config_scroll.ensureWidgetVisible(container)
            except Exception:
                pass

    def show_config_item(self, config_item: ConfigItem):
        """显示配置项"""
        # Determine the page/layout to host this config
        page = self._default_page
        layout = self._default_layout
        try:
            parent_menu_id = self.config_to_menu.get(config_item.id)
            if parent_menu_id and parent_menu_id in self.menu_pages:
                page, layout = self.menu_pages[parent_menu_id]
        except Exception:
            pass

        # 兜底：确保容器模块与布局有效
        try:
            if not hasattr(self, 'config_container_module') or self.config_container_module is None:
                self.config_container_module = ConfigContainerModule()
        except Exception:
            self.config_container_module = ConfigContainerModule()
        if layout is None:
            layout = self._default_layout
        if page is None:
            page = self._default_page

        # 如果已经存在容器，则复用，避免重复创建
        try:
            container = None
            try:
                container = self.config_container_module.containers.get(config_item.id)
            except Exception:
                container = None
            if container is None:
                container = self.config_container_module.create_container(config_item, page)
            else:
                # 更新已存在容器以反映当前值/状态
                self.config_container_module.update_container(config_item)
        except Exception:
            try:
                self._default_layout.addWidget(container)
            except Exception:
                pass

        # 连接控件变化信号（只连接一次）
        try:
            if config_item.id not in self._connected_controls:
                self._connect_control_signals(config_item)
                self._connected_controls.add(config_item.id)
        except Exception:
            pass

        # if container not managed by this layout, add it
        try:
            in_layout = layout.indexOf(container)
        except Exception:
            in_layout = -1
        if container.parent() is page and in_layout == -1:
            layout.addWidget(container)
        elif container.parent() is not page and container.parent() is not None:
            # move to page layout
            try:
                layout.addWidget(container)
            except Exception:
                try:
                    self._default_layout.addWidget(container)
                except Exception:
                    pass

        # 尝试将焦点移到新添加的容器，确保其可见性。
        try:
            container.setFocus()
        except Exception:
            try:
                # 强制刷新 UI
                try:
                    page.repaint()
                except Exception:
                    self.stacked_pages.repaint()
            except Exception:
                pass

        # 新增：连接控件信号与变化处理方法在类内部
    def _connect_control_signals(self, config_item: ConfigItem) -> None:
        """为配置控件连接变化信号，实时刷新依赖/可见性"""
        control = self.config_container_module.controls.get(config_item.id)
        if not control:
            return
        try:
            if isinstance(control, QCheckBox):
                control.stateChanged.connect(lambda _: self._on_control_changed(config_item, control.isChecked()))
            elif isinstance(control, QComboBox):
                control.currentIndexChanged.connect(lambda idx: self._on_control_changed(config_item, ["n", "m", "y"][idx]))
            elif isinstance(control, QLineEdit):
                control.editingFinished.connect(lambda: self._on_control_changed(config_item, control.text()))
            elif isinstance(control, QSpinBox):
                control.valueChanged.connect(lambda v: self._on_control_changed(config_item, v))
        except Exception:
            pass

    def _on_control_changed(self, config_item: ConfigItem, value: any) -> None:
        """控件值变化时更新数据并重新计算条件与UI"""
        # 规范化值
        t = (config_item.properties.type or "bool").lower()
        v = value
        try:
            if t == "bool":
                v = bool(value)
            elif t == "tristate":
                v = value  # 'n'/'m'/'y'
            elif t == "int":
                v = int(value)
            elif t == "hex":
                if isinstance(value, str):
                    text = value.replace("0x", "").replace("0X", "")
                    v = int(text, 16) if text else 0
        except Exception:
            pass

        # 更新模型与缓存
        try:
            self.config_values[config_item.name] = v
            config_item.current_value = v
            config_item.is_selected = self._is_selected(config_item)
        except Exception:
            pass

        # 重新计算条件
        try:
            if self.parser:
                self.parser.evaluate_conditions(self.config_values)
        except Exception:
            pass

        # 更新依赖/可见性状态
        try:
            items = list(self.parser.config_items.values()) if self.parser else []
            self.dependency_module.update_dependency_states(items, self.config_values)
            self.visibility_module.update_visibility_states(items, self.config_values)
        except Exception:
            pass

        # 更新当前容器显示
        try:
            self.config_container_module.update_container(config_item)
        except Exception:
            pass

    def clear_config_panel(self):
        """清空配置面板"""
        # 清空默认页面
        try:
            while self._default_layout.count():
                child = self._default_layout.takeAt(0)
                if child.widget():
                    child.widget().deleteLater()
        except Exception:
            pass

        # 清空所有 menu pages 的内容并从 stacked 中移除页面
        try:
            for mid, (page, layout) in list(self.menu_pages.items()):
                try:
                    while layout.count():
                        child = layout.takeAt(0)
                        if child.widget():
                            child.widget().deleteLater()
                except Exception:
                    pass
                try:
                    # remove widget from stacked and delete
                    self.stacked_pages.removeWidget(page)
                    page.deleteLater()
                except Exception:
                    pass
        except Exception:
            pass

        # reset maps
        self.menu_pages = {}
        self.config_to_menu = {}
        self.choice_to_menu = {}
        self.config_choice_parent = {}
        self.menu_anchor_widgets = {}
        self.menu_to_root = {}
        self.rendered_menu_ids = set()

        # ensure default page is visible
        try:
            self.stacked_pages.setCurrentWidget(self._default_page)
        except Exception:
            pass

        # 清空模块数据
        self.config_container_module.containers.clear()
        self.config_container_module.controls.clear()

    def on_value_out_of_range(self, config_id: str, min_val: int, max_val: int):
        """值超出范围事件"""
        config_item = self.parser.config_items.get(config_id)
        if config_item:
            self.range_module.show_range_error(
                config_item.properties.prompt or config_item.name, min_val, max_val
            )

    def save_config(self):
        """保存配置"""
        if not self.config_values:
            QMessageBox.warning(self, "Warning", "No configuration to save")
            return

        file_dialog = QFileDialog()
        file_path, _ = file_dialog.getSaveFileName(
            self, "Save Configuration", "", "Config Files (*.config);;All Files (*)"
        )

        if file_path:
            try:
                with open(file_path, "w", encoding="utf-8") as f:
                    for config_id, value in self.config_values.items():
                        config_item = self.parser.config_items.get(config_id)
                        if config_item:
                            line = f"CONFIG_{config_item.name}="
                            if isinstance(value, bool):
                                line += "y" if value else "n"
                            elif isinstance(value, str):
                                line += f'"{value}"'
                            else:
                                line += str(value)
                            f.write(line + "\n")

                self.status_bar.showMessage(f"Configuration saved to {file_path}")
            except Exception as e:
                QMessageBox.critical(
                    self, "Error", f"Failed to save configuration: {str(e)}"
                )

    # 在 KconfigMainWindow 类中添加方法
    def generate_config_files(self):
        """生成配置文件集"""
        if not self.config_values:
            QMessageBox.warning(self, "Warning", "No configuration to generate")
            return

        dir_dialog = QFileDialog()
        config_dir = dir_dialog.getExistingDirectory(
            self, "Select Output Directory", "", QFileDialog.Option.ShowDirsOnly
        )

        if config_dir:
            try:
                success = ConfigGeneratorModule.generate_config_files(
                    self.config_values, config_dir
                )

                if success:
                    QMessageBox.information(
                        self,
                        "Success",
                        f"Config files generated successfully in:\n{config_dir}",
                    )
                    self.status_bar.showMessage(
                        f"Config files generated in {config_dir}"
                    )
                else:
                    QMessageBox.critical(
                        self, "Error", "Failed to generate config files"
                    )
            except Exception as e:
                QMessageBox.critical(
                    self, "Error", f"Failed to generate config files: {str(e)}"
                )

        # 在 KconfigMainWindow 类中添加方法

    def load_dot_config(self):
        """加载.config文件"""
        file_dialog = QFileDialog()
        file_path, _ = file_dialog.getOpenFileName(
            self, "Load .config File", "", "Config Files (*.config);;All Files (*)"
        )

        if file_path:
            self.load_config_file(file_path)

        if file_path:
            self.load_config_file(file_path)

    def load_config_file(self, file_path: str):
        """加载配置文件"""
        try:
            # 解析.config文件
            loaded_values = ConfigLoaderModule.parse_dot_config(file_path)

            if not loaded_values:
                QMessageBox.warning(
                    self, "Warning", "No valid configuration found in file"
                )
                return

            # 合并配置
            self.config_values = ConfigLoaderModule.merge_configs(
                self.config_values, loaded_values, overwrite=True
            )

            # 更新数据模型
            for config_item in self.parser.config_items.values():
                if config_item.name in self.config_values:
                    config_item.current_value = self.config_values[config_item.name]
                    config_item.is_selected = self._is_selected(config_item)

            # 重新计算条件
            self.parser.evaluate_conditions(self.config_values)

            # 更新UI
            self.dependency_module.update_dependency_states(
                list(self.parser.config_items.values()), self.config_values
            )
            self.visibility_module.update_visibility_states(
                list(self.parser.config_items.values()), self.config_values
            )

            # 更新配置面板中的控件值
            for config_id, control in self.config_container_module.controls.items():
                config_item = self.parser.config_items.get(config_id)
                if config_item and config_item.name in self.config_values:
                    value = self.config_values[config_item.name]
                    self.config_container_module.set_control_value(
                        config_id, config_item.properties.type or "bool", value
                    )

            self.status_bar.showMessage(
                f"Loaded {len(loaded_values)} config values from {file_path}"
            )

        except Exception as e:
            QMessageBox.critical(self, "Error", f"Failed to load config file: {str(e)}")

    def _is_selected(self, config_item: ConfigItem) -> bool:
        """判断配置项是否被选中"""
        if config_item.properties.type == "bool":
            return bool(self.config_values.get(config_item.name, False))
        elif config_item.properties.type == "tristate":
            return self.config_values.get(config_item.name, "n") in ["y", "m"]
        else:
            return self.config_values.get(config_item.name) is not None


def main():
    """主函数"""
    # configure logging for console debugging
    logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)s %(name)s: %(message)s")

    app = QApplication(sys.argv)
    window = KconfigMainWindow()
    window.show()
    # 如果传入文件参数，启动后异步打开解析
    if len(sys.argv) > 1:
        file_to_open = sys.argv[1]
        window.current_file = file_to_open
        window.start_parse_in_background(file_to_open)
    sys.exit(app.exec())


if __name__ == "__main__":
    main()
